package com.chatplus.application.web;

import cn.hutool.core.date.DatePattern;
import com.chatplus.application.common.crypto.RequestMessageDecryptor;
import com.chatplus.application.common.crypto.RequestMessageDecryptorRSAImpl;
import com.chatplus.application.web.advice.InternalErrorResponseAttributes;
import com.chatplus.application.web.converter.EnumConverterFactory;
import com.chatplus.application.web.idempotent.concurrent.ConcurrentInterceptor;
import com.chatplus.application.web.idempotent.concurrent.ConcurrentPointcutAdvisor;
import com.chatplus.application.web.idempotent.generator.SpelExpressionValueGenerator;
import com.chatplus.application.web.idempotent.generator.ValueGenerator;
import com.chatplus.application.web.resolver.CryptoMethodArgumentResolver;
import com.chatplus.application.web.resolver.CryptoValueMethodArgumentResolver;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.format.FormatterRegistry;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import org.springframework.web.filter.CorsFilter;
import org.springframework.web.filter.ForwardedHeaderFilter;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Objects;

/**
 * Web 自动配置。
 */
@Configuration
@ComponentScan(basePackageClasses = WebAutoConfiguration.class)
@EnableConfigurationProperties
public class WebAutoConfiguration implements WebMvcConfigurer {
    public static final String CONFIG_KEY_ROOT = "chatplus.web";

    @Value("${chatplus.web.crypto.privateKey}")
    private String cryptoPrivateKey;
    @Value("${chatplus.web.crypto.publicKey}")
    private String cryptoPublicKey;

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(encryptedRequestParamMethodArgumentResolver());
        resolvers.add(cryptoMethodArgumentResolver());
    }

    @Bean
    public HandlerMethodArgumentResolver encryptedRequestParamMethodArgumentResolver() {
        return new CryptoValueMethodArgumentResolver(requestMessageDecryptor());
    }

    @Bean
    public HandlerMethodArgumentResolver cryptoMethodArgumentResolver() {
        return new CryptoMethodArgumentResolver(requestMessageDecryptor());
    }

    @Bean
    public RequestMessageDecryptor requestMessageDecryptor() {
        Objects.requireNonNull(cryptoPublicKey);
        Objects.requireNonNull(cryptoPrivateKey);
        return new RequestMessageDecryptorRSAImpl(cryptoPrivateKey, cryptoPublicKey);
    }

    @Bean
    public FilterRegistrationBean<ForwardedHeaderFilter> registerForwardedHeaderFilter() {
        ForwardedHeaderFilter filter = new ForwardedHeaderFilter();
        filter.setRemoveOnly(false);

        FilterRegistrationBean<ForwardedHeaderFilter> registrationBean = new FilterRegistrationBean<>();
        registrationBean.setFilter(filter);
        registrationBean.setOrder(Ordered.HIGHEST_PRECEDENCE);
        return registrationBean;
    }

    @Bean
    public InternalErrorResponseAttributes myErrorResponseAttributes() {
        return new InternalErrorResponseAttributes();
    }

    @Override
    public void addFormatters(FormatterRegistry registry) {
        registry.addConverterFactory(new EnumConverterFactory());
    }

    // 设置默认的 JSON 输出策略
    // 自动将驼峰转为下划线
    @Bean
    public ObjectMapper objectMapper() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
                .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
                .registerModule(new JavaTimeModule());
        objectMapper.setDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_MS_PATTERN));
        return objectMapper;
    }

    @Bean
    @ConditionalOnMissingBean(ValueGenerator.class)
    ValueGenerator keyGenerator() {
        return new SpelExpressionValueGenerator();
    }

    @Bean
    @ConditionalOnMissingBean(ConcurrentInterceptor.class)
    ConcurrentInterceptor concurrentInterceptor(RedissonClient redissonClient, ValueGenerator keyGenerator) {
        return new ConcurrentInterceptor(redissonClient, keyGenerator);
    }

    @Bean
    ConcurrentPointcutAdvisor concurrentPointcutAdvisor(ConcurrentInterceptor concurrentInterceptor) {
        return new ConcurrentPointcutAdvisor(concurrentInterceptor);
    }

    // 配置跨域请求
    // TODO 后续重构完成要找一下为啥前端要这样设计，前端设置了crossorigin: "anonymous"导致这里只能这样设置
    @Bean
    public CorsFilter corsFilter() {
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        CorsConfiguration config = new CorsConfiguration();
        // 允许发送Cookie
        config.setAllowCredentials(true);
        // 允许的请求方法，如 GET、POST 等
        config.addAllowedMethod("*");
        // 允许所有域访问，实际应用中建议根据需要设置具体的域
        config.addAllowedOriginPattern("*");
        // 允许的请求头
        config.addAllowedHeader("*");
        source.registerCorsConfiguration("/**", config);
        return new CorsFilter(source);
    }
}
